package com.ruoyi.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.project.domain.entity.*;
import com.ruoyi.project.domain.query.*;
import com.ruoyi.project.mapper.*;
import com.ruoyi.project.service.*;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;


@Service
public class ProjectAccountingServiceImpl extends ServiceImpl<ProjectAccountingMapper, ProjectAccounting> implements IProjectAccountingService {

    @Resource
    private ProjectAccountingMapper projectAccountingMapper;

    @Resource
    private IProjectCostingService projectCostingService;

    @Resource
    private IProjectGoodsService projectGoodsService;

    @Resource
    private IProjectStoreService projectStoreService;


    @Resource
    private IProjectSaleService projectSaleService;

    @Resource
    private IProjectService projectService;

    @Resource
    private IPhaseDictionaryService dictionaryService;

    @Autowired
    private RedisCache redisCache;

    @Resource
    private IProjectAccountingService accountingService;

    @Resource
    private IProjectWarrantyService warrantyService;

    @Resource
    private IProjectCustomerService customerService;

    @Resource
    private IProjectFeeService feeService;

    @Resource
    private IProjectFinanceService financeService;

    @Autowired
    private ProjectSaleMapper projectSaleMapper;

    @Resource
    private ProjectDetailLogMapper projectDetailLogMapper;

    @Resource
    private ProjectCustomMapper projectCustomMapper;


    @Autowired
    private ProjectStoreMapper projectStoreMapper;


    @Resource
    private ProjectSaleLogMapper projectSaleLogMapper;

    @Resource
    private IProjectSaleLogService projectSaleLogService;

    @Resource
    private IProjectSaleConfigService saleConfigService;


    //分页
//    @Override
//    public TableDataInfo getPage(ProjectParticipanQuery query) {
//        Page<Object> page = PageHelper.startPage(query.getPageNum(),query.getPageSize());
//        List<ProjectParticipan> list = projectAccountingMapper.getList(query);
//        return new TableDataInfo(list, page.getTotal());
//    }

    //新增
    @Override
    public Boolean addInfo(ProjectAccounting project) {
        //判断 是吴总就直接设置为总经理已确认的
        //1未提交 2已提交 3已撤回 4总经理已确认
        Long userId = SecurityUtils.getUserId();
        if (userId.equals(100L)) {
            project.setAudit("4");
        } else {
            //新增默认设置为1
            project.setAudit("1");
        }

        //如果是往来客户的  默认审核通过
        if ("Y".equals(project.getIsCustomer())) {
            project.setAudit("4");
            //重新计算所有信息
            return setNewInfo(project, "add");
        }
        project.setOperator(SecurityUtils.getLoginUser().getUser().getNickName());
        /*
        项目税额=结算金额/(1+税率)*税率；（产品销售13%、工程安装费9%、技术服务费6%）
        毛利润=结算金额-项目材料成本-其他成本1一其他成本2-其他成本3 - 外协人工成本 - 资金成本
        项目成本税费=(项目材料成本 + 其他成本1 + 其他成本2 + 其他成本3 + 外协人工成本 + 资金成本)/(1+税率)*税率
        差额税费=项目税额-项目成本税费
        税前利润=毛利润-差额税费
        纯利润扣税金=税前利润*80%   （产品销售）
        纯利润扣税金=税前利润*85%   （工程安装费）
        纯利润扣税金=税前利润*88%   （技术服务费）

        项目提成=纯利润扣税金*25%
        */

        Long phaseId = project.getPhaseId();
        if (phaseId == null) {
            throw new RuntimeException("后端接口异常");
        }
        PhaseDictionary byId = dictionaryService.getById(project.getPhaseId());
        project.setPhaseName(byId.getPhaseName());

        ProjectAccounting calculate = calculate(project);
        ProjectAccounting copy = SerializationUtils.clone(calculate);
        BigDecimal newAmount = project.getProjectAmount().subtract(
                copy.getRetentionMoney() != null ? copy.getRetentionMoney() : BigDecimal.ZERO
        );
        copy.setProjectAmount(newAmount);

        copy.setProjectAmount(project.getProjectAmount().subtract(copy.getRetentionMoney() != null ? copy.getRetentionMoney() : BigDecimal.ZERO));
        ProjectAccounting newCalculate = calculate(copy);
        calculate.setCommissionWarranty(newCalculate.getProjectCommission());
        return this.save(calculate);
    }

    @Override
    public Boolean updateInfo(ProjectAccounting project) {

        //判断绩效金额有无变化
        //如果是审核通过了再修改
        if (project.getMainId() != null) {
            //重新计算所有信息
            return setNewInfo(project, "update");
        }

        //判断 是吴总就直接设置为总经理已确认的
        //1未提交 2已提交 3已撤回 4总经理已确认
        Long userId = SecurityUtils.getUserId();
        if (userId.equals(100L)) {
            project.setAudit("4");
        }


        project.setOperator(SecurityUtils.getLoginUser().getUser().getNickName());
        ProjectAccounting calculate = calculate(project);
        ProjectAccounting copy = SerializationUtils.clone(calculate);
        BigDecimal newAmount = project.getProjectAmount().subtract(
                copy.getRetentionMoney() != null ? copy.getRetentionMoney() : BigDecimal.ZERO
        );
        copy.setProjectAmount(newAmount);

        copy.setProjectAmount(project.getProjectAmount().subtract(copy.getRetentionMoney() != null ? copy.getRetentionMoney() : BigDecimal.ZERO));
        ProjectAccounting newCalculate = calculate(copy);
        calculate.setCommissionWarranty(newCalculate.getProjectCommission());
        return this.updateById(calculate);
    }

    @Override
    public Boolean setNewInfo(ProjectAccounting project, String type) {
        //1.重新计算设置此类型的结算总额(销售总金额)  材料成本
        ProjectCustomQuery query = new ProjectCustomQuery();
        query.setId(project.getMainId());
        query.setRefresh("2");

        ProjectCustom queryCustom = projectCustomMapper.selectById(project.getMainId());
        query.setStartDate(queryCustom.getStartDate());
        query.setEndDate(queryCustom.getEndDate());

        Map<String, Object> map = saleInfo(query);
        ProjectCustom projectCustom = (ProjectCustom) map.get("projectCustom");
        //销售总金额
        BigDecimal total = projectCustom.getTotal() != null ? projectCustom.getTotal() : BigDecimal.ZERO;
        project.setProjectAmount(total);

        //成本
        BigDecimal costTotal = projectCustom.getCostTotal() != null ? projectCustom.getCostTotal() : BigDecimal.ZERO;
        project.setMaterialCost(costTotal);
        project.setOperator(SecurityUtils.getLoginUser().getUser().getNickName());

        ProjectAccounting calculate = calculate(project);

        //重新设置projectCustomer的信息
        setCustomMoney(calculate, projectCustom);
        if ("add".equals(type)) {
            return this.save(calculate);
        } else {
            return this.updateById(calculate);
        }

    }

    //设置金额信息
    public void setCustomMoney(ProjectAccounting project, ProjectCustom projectCustom) {
        BigDecimal total = BigDecimal.ZERO;
        BigDecimal personTotal = BigDecimal.ZERO;
        BigDecimal deptTotal = BigDecimal.ZERO;

        total = project.getProjectCommission() != null ? project.getProjectCommission() : BigDecimal.ZERO;

        //总提成total  乘以百分之八十得到个人绩效总金额  personTotal 剩下百分之二十为deptTotal

        // 计算个人绩效（80%）
        personTotal = total.multiply(BigDecimal.valueOf(0.8));

        // 计算部门绩效（20%）
        deptTotal = total.multiply(BigDecimal.valueOf(0.2));

        // 可选：保留两位小数，根据业务需要设置舍入模式
        personTotal = personTotal.setScale(2, RoundingMode.HALF_UP);
        deptTotal = deptTotal.setScale(2, RoundingMode.HALF_UP);
        projectCustom.setPersonTotal(personTotal);
        projectCustom.setDeptTotal(deptTotal);

        //重新设置一下已发放和待发放 根据日志表中计算 更新日志表中的个人部门总金额和待发放金额
        LambdaQueryWrapper<ProjectDetailLog> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(ProjectDetailLog::getProjectNo, projectCustom.getCustomerId());
        deleteWrapper.orderByAsc(ProjectDetailLog::getCreateTime);
        List<ProjectDetailLog> projectDetailLogs = projectDetailLogMapper.selectList(deleteWrapper);

        //个人已发放
        BigDecimal logPersonAlready = BigDecimal.ZERO;
        //部门已发放
        BigDecimal logDeptAlready = BigDecimal.ZERO;
        for (ProjectDetailLog detailLog : projectDetailLogs) {
            logPersonAlready = logPersonAlready.add(detailLog.getPersonCurrent() != null ? detailLog.getPersonCurrent() : BigDecimal.ZERO);
            logDeptAlready = logDeptAlready.add(detailLog.getDeptCurrent() != null ? detailLog.getDeptCurrent() : BigDecimal.ZERO);
        }

        //重新计算 个人 部门 待发放 和个人 部门 总绩效
        BigDecimal personWait = personTotal.subtract(logPersonAlready);
        BigDecimal deptWait = deptTotal.subtract(logDeptAlready);

        //已发放
        projectCustom.setDeptAlready(logDeptAlready);
        projectCustom.setPersonAlready(logPersonAlready);

        //待发放 和主表保持一致
        projectCustom.setPersonWait(personWait);
        projectCustom.setDeptWait(deptWait);

        //个人剩余
        BigDecimal perCurrentTotal = BigDecimal.ZERO;
        //部门剩余
        BigDecimal deptCurrentTotal = BigDecimal.ZERO;
        //计算更新每一笔最新的个人剩余和部门剩余
        for (ProjectDetailLog detailLog : projectDetailLogs) {
            detailLog.setDeptTotalt(deptTotal);
            detailLog.setPersonTotalt(personTotal);
            //累加
            perCurrentTotal = perCurrentTotal.add(detailLog.getPersonCurrent() != null ? detailLog.getPersonCurrent() : BigDecimal.ZERO);
            deptCurrentTotal = deptCurrentTotal.add(detailLog.getDeptCurrent() != null ? detailLog.getDeptCurrent() : BigDecimal.ZERO);

            BigDecimal newPersonWait = personTotal.subtract(perCurrentTotal);
            BigDecimal newDeptWait = deptTotal.subtract(deptCurrentTotal);

            detailLog.setDeptWait(newDeptWait);
            detailLog.setPersonWait(newPersonWait);
            projectDetailLogMapper.updateById(detailLog);
        }

        projectCustomMapper.updateById(projectCustom);
    }

    @Override
    public Boolean deleteInfo(String id) {

        ProjectAccounting project = getInfoById(id);
        if (project.getMainId() != null) {
            ProjectCustom projectCustom = projectCustomMapper.selectById(project.getMainId());

            //总提成total  乘以百分之八十得到个人绩效总金额  personTotal 剩下百分之二十为deptTotal
            projectCustom.setPersonTotal(BigDecimal.ZERO);
            projectCustom.setDeptTotal(BigDecimal.ZERO);

            //重新设置一下已发放和待发放 删除日志表
            projectCustom.setDeptAlready(BigDecimal.ZERO);
            projectCustom.setPersonAlready(BigDecimal.ZERO);
            projectCustom.setDeptWait(BigDecimal.ZERO);
            projectCustom.setPersonWait(BigDecimal.ZERO);

            LambdaQueryWrapper<ProjectDetailLog> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.eq(ProjectDetailLog::getProjectNo, projectCustom.getCustomerId());
            projectDetailLogMapper.delete(deleteWrapper);


            projectCustomMapper.updateById(projectCustom);

        }

        return this.removeById(id);
    }

    @Override
    public ProjectAccounting getInfoById(String id) {
        return this.getById(id);
    }

    @Override
    public List<ProjectAccounting> getList(ProjectAccountingQuery query) {
        LambdaQueryWrapper<ProjectAccounting> wrapper = new QueryWrapper<ProjectAccounting>().lambda();
        wrapper.eq(query.getProjectNo() != null, ProjectAccounting::getProjectNo, query.getProjectNo());

        Long userId = SecurityUtils.getUserId();
        //吴总 只看已提交的和确认的
        if (userId.equals(100L)) {
            wrapper.in(ProjectAccounting::getAudit, Arrays.asList("2", "4"));
        }
        List<ProjectAccounting> list = this.list(wrapper);
        for (ProjectAccounting projectAccounting : list) {
            String audit = projectAccounting.getAudit();

            if (("2".equals(audit) || "4".equals(audit)) && !userId.equals(100L)) {
                projectAccounting.setRowStatus("1");
            }

            if (userId.equals(100L) && "4".equals(audit)) {
                projectAccounting.setRowStatus("1");
            }
        }
        return list;
    }

    @Override
    public void checkWait(ProjectAccountingQuery query) {
        //检查是否有待提交的
        LambdaQueryWrapper<ProjectAccounting> wrapper = new QueryWrapper<ProjectAccounting>().lambda();
        wrapper.eq(ProjectAccounting::getProjectNo, query.getProjectNo());
        wrapper.in(ProjectAccounting::getAudit, Arrays.asList("1", "2", "3"));
        List<ProjectAccounting> list = this.list(wrapper);
        if (list != null && list.size() > 0) {
            throw new RuntimeException("有待提交的利润计算信息,请联系销售负责人提交");
        }
    }

    @Override
    public List<ProjectAccounting> listCustomer(ProjectAccountingQuery query) {

        if (query.getMainId() == null || "".equals(query.getMainId())) {
            throw new RuntimeException("往来数据查询错误,请联系管理员");
        }

        LambdaQueryWrapper<ProjectAccounting> wrapper = new QueryWrapper<ProjectAccounting>().lambda();
        wrapper.eq(query.getMainId() != null, ProjectAccounting::getMainId, query.getMainId());

        Long userId = SecurityUtils.getUserId();
        //吴总 只看已提交的和确认的
        if (userId.equals(100L)) {
            wrapper.in(ProjectAccounting::getAudit, Arrays.asList("2", "4"));
        }
        List<ProjectAccounting> list = this.list(wrapper);
        for (ProjectAccounting projectAccounting : list) {
            String audit = projectAccounting.getAudit();

            if (("2".equals(audit) || "4".equals(audit)) && !userId.equals(100L)) {
                projectAccounting.setRowStatus("1");
            }

            if (userId.equals(100L) && "4".equals(audit)) {
                projectAccounting.setRowStatus("1");
            }
        }
        return list;
    }


    @Override
    public Map getTotalCost(ProjectAccountingQuery query) {
        Map map = new HashMap<>();
        Project project = projectService.getInfoById(query.getProjectNo());
        String housekeeperProject = project.getHousekeeperProject();
        if (StringUtils.isEmpty(housekeeperProject)) {
            throw new RuntimeException("未关联管家婆项目");
        }

        //1表示重新计算 否则直接取缓存中的
        if (!"1".equals(query.getRefresh())) {
            map = (Map) redisCache.getCacheObject(housekeeperProject + "cost");
            if (map != null) {
                return map;
            } else {
                map = new HashMap<>();
            }
        }
        ProjectCostingQuery costingQuery = new ProjectCostingQuery();
        costingQuery.setProjectNo(query.getProjectNo());
        List<ProjectCosting> costingList = projectCostingService.getList(costingQuery);

        // 1.获取这个管家婆项目所有出库单 按照时间从早到晚
        ProjectSaleQuery saleQuery = new ProjectSaleQuery();
        saleQuery.setHousekeeperProject(housekeeperProject);
        List<ProjectSale> saleListGjp = projectSaleService.getAllGjpList(saleQuery);

        //和出库单做比较 有异常的  做标记
        compareToCosting(costingList, saleListGjp, query.getProjectNo());


        fifoForBatchAll(saleListGjp);
        BigDecimal totalCost = BigDecimal.ZERO;
        for (ProjectSale sale : saleListGjp) {
            totalCost = totalCost.add(sale.getCostTotal() != null ? sale.getCostTotal() : BigDecimal.ZERO);
        }


        map.put("totalCost", totalCost.setScale(2, RoundingMode.HALF_UP).abs());
        map.put("saleList", saleListGjp);
        // 返回保留两位小数的总成本(确保为正数)
        System.out.println("最后总成本" + totalCost);
        redisCache.redisTemplate.opsForValue().set(housekeeperProject + "cost", map, 12, TimeUnit.HOURS);

        return map;
    }


    private void fifoForBatchAll(List<ProjectSale> saleList) {
        if (saleList == null || saleList.isEmpty()) {
            return;
        }

        // 1. 预处理：过滤有效数据并收集查询条件
        List<ProjectSale> validSales = new ArrayList<>();
        Set<String> goodsIds = new HashSet<>();
        Set<Date> saleDates = new HashSet<>();
        Map<String, Date> goodsSaleMap = new HashMap<>();

        for (ProjectSale sale : saleList) {
            if (sale.getGoodsId() == null) {
                sale.setStatus("1");
                continue;
            }

            String goodsId = sale.getGoodsId();
            if (StringUtils.isEmpty(goodsId)) {
                continue;
            }


            LambdaQueryWrapper<ProjectStore> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(ProjectStore::getPrice)             // 只查 price 字段
                    .eq(ProjectStore::getGoodsId, goodsId)
                    .eq(ProjectStore::getDelFlag, "0")           // 建议加上删除标记
                    .groupBy(ProjectStore::getPrice)            // 按 price 去重
                    .orderByAsc(ProjectStore::getPrice);        // 升序排序

            List<ProjectStore> result = projectStoreMapper.selectList(wrapper);

// 提取 price 列表（如果只需要 List<BigDecimal>）
            List<BigDecimal> costList = result.stream()
                    .map(ProjectStore::getPrice)
                    .collect(Collectors.toList());
            costList.add(BigDecimal.ZERO);
            sale.setCostList(costList);

            if ("00021".equals(goodsId)) {
                sale.setCostTotal(BigDecimal.ZERO);
                setInvoice(sale);
                continue;
            }

            if (sale.getAmount() != null && sale.getAmount() < 0) {
                sale.setAmount(-sale.getAmount());
            } else {
                //冲销的
                sale.setStatus("2");
            }

            validSales.add(sale);
            goodsIds.add(goodsId);
            saleDates.add(sale.getSaleDate());

            //使用map
            if (goodsSaleMap.containsKey(goodsId)) {
                Date existingDate = goodsSaleMap.get(goodsId);
                Date saleDate = sale.getSaleDate();
                // 如果 saleDate 更晚（更大），则更新
                if (saleDate != null && (existingDate == null || saleDate.compareTo(existingDate) > 0)) {
                    goodsSaleMap.put(goodsId, saleDate);
                }
            } else {
                // 如果 map 中没有该 goodsId，直接放入
                goodsSaleMap.put(goodsId, sale.getSaleDate());
            }

        }

        if (validSales.isEmpty()) {
            return;
        }

        // 2. 批量查询所有需要的数据
        Map<String, List<ProjectSale>> saleHistoryMap = batchQuerySales(goodsIds, goodsSaleMap);
        Map<String, List<ProjectStore>> storeHistoryMap = batchQueryStores(goodsIds, goodsSaleMap);

        // 3. 预计算累计数量（按商品分组）
        Map<String, Integer> saleAccumulatedMap = preCalculateSaleAccumulated(saleHistoryMap);
        Map<String, TreeMap<Date, StoreAccumulated>> storeAccumulatedMap = preCalculateStoreAccumulated(storeHistoryMap);

        // 4. 批量处理有效销售记录
        processValidSales(validSales, saleAccumulatedMap, storeAccumulatedMap);
    }

    // 批量查询销售历史记录
    private Map<String, List<ProjectSale>> batchQuerySales(Set<String> goodsIds, Map<String, Date> saleDates) {
        if (goodsIds.isEmpty()) {
            return new HashMap<>();
        }
        List<ProjectSale> allSales = new ArrayList<>();
        for (String goodsId : goodsIds) {
            Date maxDate = saleDates.get(goodsId);
            LambdaQueryWrapper<ProjectSale> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(ProjectSale::getGoodsId, goodsId);
            wrapper.le(ProjectSale::getSaleDate, maxDate);
            List<ProjectSale> saleList = projectSaleMapper.selectList(wrapper);
            allSales.addAll(saleList);
        }


        return allSales.stream()
                .collect(Collectors.groupingBy(ProjectSale::getGoodsId));
    }

    // 批量查询入库历史记录
    private Map<String, List<ProjectStore>> batchQueryStores(Set<String> goodsIds, Map<String, Date> saleDates) {
        if (goodsIds.isEmpty()) {
            return new HashMap<>();
        }
        List<ProjectStore> allStores = new ArrayList<>();

        for (String goodsId : goodsIds) {
            Date maxDate = saleDates.get(goodsId);
            LambdaQueryWrapper<ProjectStore> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(ProjectStore::getGoodsId, goodsIds);
            wrapper.le(ProjectStore::getStorageDate, maxDate);
            List<ProjectStore> storeList = projectStoreService.list(wrapper);
            allStores.addAll(storeList);

        }


        return allStores.stream()
                .collect(Collectors.groupingBy(ProjectStore::getGoodsId));
    }

    // 预计算销售累计数量
    private Map<String, Integer> preCalculateSaleAccumulated(Map<String, List<ProjectSale>> saleHistoryMap) {
        Map<String, Integer> result = new HashMap<>();

        for (Map.Entry<String, List<ProjectSale>> entry : saleHistoryMap.entrySet()) {
            String goodsId = entry.getKey();
            List<ProjectSale> sales = entry.getValue();

            // 按日期排序
            sales.sort(Comparator.comparing(ProjectSale::getSaleDate));

            int accumulated = 0;
            for (ProjectSale sale : sales) {
                // 转为正数计算（与原逻辑一致）
                Integer historyCount = -sale.getAmount();
                accumulated += historyCount;
            }

            result.put(goodsId, accumulated);
        }

        return result;
    }

    // 预计算入库累计数量（使用TreeMap便于范围查询）
    private Map<String, TreeMap<Date, StoreAccumulated>> preCalculateStoreAccumulated(
            Map<String, List<ProjectStore>> storeHistoryMap) {

        Map<String, TreeMap<Date, StoreAccumulated>> result = new HashMap<>();

        for (Map.Entry<String, List<ProjectStore>> entry : storeHistoryMap.entrySet()) {
            String goodsId = entry.getKey();
            List<ProjectStore> stores = entry.getValue();

            // 按日期排序
            stores.sort(Comparator.comparing(ProjectStore::getStorageDate));

            TreeMap<Date, StoreAccumulated> accumulatedMap = new TreeMap<>();
            int totalAccumulated = 0;

            for (ProjectStore store : stores) {
                totalAccumulated += store.getAmount();
                accumulatedMap.put(store.getStorageDate(),
                        new StoreAccumulated(store.getPrice(), totalAccumulated));
            }

            result.put(goodsId, accumulatedMap);
        }

        return result;
    }

    private void processValidSales(List<ProjectSale> validSales,
                                   Map<String, Integer> saleAccumulatedMap,
                                   Map<String, TreeMap<Date, StoreAccumulated>> storeAccumulatedMap) {

        for (ProjectSale sale : validSales) {
            String goodsId = sale.getGoodsId();
            Date saleDate = sale.getSaleDate();

            // 获取该商品的累计销售数量
            Integer allSaleCount = saleAccumulatedMap.getOrDefault(goodsId, 0);

            // 获取该商品的入库累计信息
            TreeMap<Date, StoreAccumulated> storeMap = storeAccumulatedMap.get(goodsId);

            if (storeMap != null) {
                // 找到第一个入库日期大于等于销售日期的记录（ceilingEntry）
                Map.Entry<Date, StoreAccumulated> entry = storeMap.ceilingEntry(saleDate);

                if (entry != null && entry.getValue().accumulatedAmount >= allSaleCount) {
                    sale.setCostPrice(entry.getValue().price);
                    sale.setOldCostPrice(entry.getValue().price);//记录原始成本
                    sale.setCostString(entry.getValue().price.toString());
                } else {
                    // 如果没有匹配到，使用最后一批的价格
                    Map.Entry<Date, StoreAccumulated> lastEntry = storeMap.lastEntry();
                    if (lastEntry != null) {
                        sale.setCostPrice(lastEntry.getValue().price);
                        sale.setOldCostPrice(lastEntry.getValue().price);//记录原始成本
                        sale.setCostString(lastEntry.getValue().price.toString());

                    }
                }
            } else {
                sale.setOldCostPrice(sale.getCostPrice());
            }


            //查询有没有自定义选过成本 有就使用自定义选过的
            ProjectSaleLog log = new ProjectSaleLog();
            log.setHousekeeperProject(sale.getHousekeeperProject());
            log.setGoodsId(goodsId);
            log.setSaleDate(saleDate);
            log.setAmount(-sale.getAmount());//上面取过反 这里改回来
            log.setOrders(sale.getOrders());
            List<ProjectSaleLog> logList = projectSaleLogService.selectProjectSaleOne(log);
            if (logList != null && logList.size() > 0) {
                BigDecimal newCost = logList.get(logList.size() - 1).getNewCost();
                sale.setCostPrice(newCost);
                sale.setCostString(newCost.toString());
                sale.setLogList(logList);

                sale.setOldCostPrice(newCost);//重新设置原始成本
            }


            // 判断销售总金额是否在 -1元 和 1元之间（不包含 -1 和 1），如果是，则不计入成本
            BigDecimal total = sale.getTotal();

// 定义边界值
            BigDecimal lowerBound = new BigDecimal("-1"); // -1元
            BigDecimal upperBound = BigDecimal.ONE;      // 1元

            //2025.11.03 销售总金额小于一毛钱的也需要有成本
            if (total != null
                    && total.compareTo(lowerBound) > 0   // total > -1
                    && total.compareTo(upperBound) < 0) { // total < 1
                // 金额在 (-1, 1) 范围内，不计入成本
                // 例如：记录日志或跳过成本计算
//                sale.setCostPrice(BigDecimal.ZERO);
//                sale.setCostTotal(BigDecimal.ZERO);
//                sale.setOldCostPrice(BigDecimal.ZERO);
            } else {
                // 金额 <= -1 或 >= 1，计入成本
                // 执行成本计算逻辑

            }


            // 设置成本合计
            if (sale.getCostPrice() != null) {
                sale.setCostTotal(sale.getCostPrice().multiply(BigDecimal.valueOf(sale.getAmount())));
            }

            // 如果是冲销记录，恢复负数
            if ("2".equals(sale.getStatus())) {
                sale.setStatus("1");
                sale.setAmount(-sale.getAmount());
                if (sale.getCostTotal() != null) {
                    sale.setCostTotal(sale.getCostTotal().negate());
                }
            }

            // 计算利润和毛利率
            calculateProfit(sale);
        }
    }

    // 辅助类，存储入库累计信息
    private static class StoreAccumulated {
        BigDecimal price;
        int accumulatedAmount;

        StoreAccumulated(BigDecimal price, int accumulatedAmount) {
            this.price = price;
            this.accumulatedAmount = accumulatedAmount;
        }
    }

    // 利润计算（与原逻辑一致）
    private void calculateProfit(ProjectSale sale) {
        sale.setTotal(sale.getTotal().negate());

        BigDecimal total = (sale.getTotal() == null) ? BigDecimal.ZERO : sale.getTotal();
        BigDecimal grossProfit = total.subtract(sale.getCostTotal() == null ? BigDecimal.ZERO : sale.getCostTotal());
        sale.setProfit(grossProfit);

        if (total.compareTo(BigDecimal.ZERO) != 0) {
            BigDecimal grossMargin = grossProfit.divide(total, 2, RoundingMode.HALF_UP);
            BigDecimal profitRatio = grossMargin.multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);
            sale.setProfitRatio(profitRatio);
        }
    }


    public void setInvoice(ProjectSale sale) {
        //重新设置一下名称 把发票号拼接上
        String code = sale.getCode();
        LambdaQueryWrapper<ProjectFfinance> financeLambdaQueryWrapper = new QueryWrapper<ProjectFfinance>().lambda();
        financeLambdaQueryWrapper.eq(ProjectFfinance::getCode, code);
        List<ProjectFfinance> ffinanceList = financeService.list(financeLambdaQueryWrapper);
        String name = sale.getFullName();
        StringBuilder resultBuilder = new StringBuilder();

        for (ProjectFfinance projectFfinance : ffinanceList) {
            String finance = projectFfinance.getFinance(); // 假设值为 "24.07.26/54537634"
            int lastIndex = finance.lastIndexOf('/');

            if (lastIndex != -1) {
                String result = finance.substring(lastIndex + 1);

                if (resultBuilder.length() > 0) {
                    resultBuilder.append(","); // 添加逗号
                }
                resultBuilder.append(result); // 添加内容
            }
        }
        String finalResult = resultBuilder.toString(); // 最终结果：54537634,12345678,...
        sale.setFullName(name + "(发票编号: " + finalResult + ")");
        sale.setName("发票金额(元):" + sale.getInvoiceAmount());

    }

    public void compareToCosting(List<ProjectCosting> costingList, List<ProjectSale> saleList, String projectNo) {
        for (ProjectSale sale : saleList) {

            //1.单价小于1毛的标记
            BigDecimal price = sale.getPrice();
            if (price != null && price.compareTo(new BigDecimal("0.1")) <= 0) {
                sale.setStatus("1");
            }


            //2.销售总金额小于1毛的标记 (负数 先取反)
            BigDecimal total = sale.getTotal();
            if (total != null) {
                BigDecimal absoluteTotal = total.abs(); // 取绝对值
                if (absoluteTotal.compareTo(new BigDecimal("0.1")) <= 0) {
                    sale.setStatus("1"); // 销售总金额小于等于 0.1 元时标记
                }
            }

        }

    }

    @Override
    public Map<String, BigDecimal> getAllList(String projectNo) {
        LambdaQueryWrapper<ProjectAccounting> wrapper = new QueryWrapper<ProjectAccounting>().lambda()
                .eq(ProjectAccounting::getProjectNo, projectNo)
                .eq(ProjectAccounting::getAudit, 4);//已审核通过的

        List<ProjectAccounting> list = accountingService.list(wrapper);
        Map<String, BigDecimal> map = new HashMap<>();
        //项目提成
        BigDecimal allCommission = BigDecimal.ZERO;
        BigDecimal allCommissionWarranty = BigDecimal.ZERO;
        for (ProjectAccounting projectAccounting : list) {
//            if ("2".equals(projectAccounting.getAudit())) {
//                throw new RuntimeException("有待审核的利润计算信息,请先通过审核后再进行绩效分配");
//            }
            //累加所有项目提成
            allCommission = allCommission.add(projectAccounting.getProjectCommission());
        }

        LambdaQueryWrapper<ProjectWarranty> warrantyWrapper = new QueryWrapper<ProjectWarranty>().lambda()
                .eq(ProjectWarranty::getProjectNo, projectNo);
        //查询出质保金实收
        List<ProjectWarranty> warrantyList = warrantyService.list(warrantyWrapper);

        //质保金 实收
        BigDecimal receivedWarranty = BigDecimal.ZERO;
        for (ProjectWarranty warranty : warrantyList) {
            BigDecimal receivedAmount = warranty.getReceivedAmount();
            receivedWarranty = receivedWarranty.add(receivedAmount != null ? receivedAmount : BigDecimal.ZERO);
        }

        //使用项目提成做绩效分配一次
        //使用质保金乘以0.25再分配一次
        BigDecimal baseCommission = receivedWarranty.multiply(new BigDecimal("0.25"));

        map.put("allCommission", allCommission);
        map.put("allCommissionWarranty", baseCommission);
        return map;
    }

    @Override
    public Map<String, BigDecimal> getAllListBefore(String projectNo) {
        Map<String, BigDecimal> map = new HashMap<>();
        //项目提成
        BigDecimal allCommission = BigDecimal.ZERO;
        ProjectAccounting projectAccounting = new ProjectAccounting();

        //获取项目材料成本 报销成本 结算金额

//        projectAccounting
//        calculate()
        BigDecimal allCommissionWarranty = BigDecimal.ZERO;
//            allCommission =projectAccounting.getProjectCommission()

        LambdaQueryWrapper<ProjectWarranty> warrantyWrapper = new QueryWrapper<ProjectWarranty>().lambda()
                .eq(ProjectWarranty::getProjectNo, projectNo);
        //查询出质保金实收
        List<ProjectWarranty> warrantyList = warrantyService.list(warrantyWrapper);

        //质保金 实收
        BigDecimal receivedWarranty = BigDecimal.ZERO;
        for (ProjectWarranty warranty : warrantyList) {
            BigDecimal receivedAmount = warranty.getReceivedAmount();
            receivedWarranty = receivedWarranty.add(receivedAmount != null ? receivedAmount : BigDecimal.ZERO);
        }

        //使用项目提成做绩效分配一次
        //使用质保金乘以0.25再分配一次
        BigDecimal baseCommission = receivedWarranty.multiply(new BigDecimal("0.25"));

        map.put("allCommission", allCommission);
        map.put("allCommissionWarranty", baseCommission);
        return map;
    }


    @Override
    public Map<String, Object> saleInfo(ProjectCustomQuery query) {
        Map<String, Object> map = new HashMap<>();
        //1.查询详细
        ProjectCustom projectCustom = customerService.getById(query.getId());

        //从缓存中取
        if ("1".equals(query.getRefresh())) {
            Object cacheObject = redisCache.getCacheObject(String.valueOf(projectCustom.getId()));
            if (cacheObject != null) {
                return (Map<String, Object>) redisCache.getCacheObject(String.valueOf(projectCustom.getId()));
            }
        }
        //2.取出往来客户id 起始日期 截止日期
        String customerId = projectCustom.getCustomerId();
        Date startDate = projectCustom.getStartDate();
        Date endDate = projectCustom.getEndDate();

        //3.查询范围内的出库单
//        LambdaQueryWrapper<ProjectSale> saleWrapper = new LambdaQueryWrapper<>();
//        saleWrapper.eq(ProjectSale::getHousekeeperProject, customerId);
//        saleWrapper.between(ProjectSale::getSaleDate, startDate, endDate);
//        List<ProjectSale> saleList = projectSaleService.list(saleWrapper);
        List<ProjectSale> saleList = projectSaleMapper.getAllSaleCustom(customerId, projectCustom.getStartDate(), projectCustom.getEndDate());

        //4.直接累加 或者调用算法
        ProjectFeeQuery feeQuery = new ProjectFeeQuery();
        feeQuery.setHousekeeperProject(customerId);
        List<ProjectFee> feeList = feeService.getOtherFee(feeQuery);
        for (ProjectFee projectFee : feeList) {
            //其他收入里面  还需要过滤掉不在日期范围内的
            // 获取三个日期
            Date submitDate = projectFee.getSubmitDate();

// 判断 submitDate 是否在 [startDate, endDate] 范围内（包含边界）
            boolean isWithinRange = false;

            if (submitDate != null && startDate != null && endDate != null) {
                isWithinRange = !submitDate.before(startDate) && !submitDate.after(endDate);
                // 等价于：
                isWithinRange = (submitDate.compareTo(startDate) >= 0) && (submitDate.compareTo(endDate) <= 0);
                if (!isWithinRange) {
                    continue;
                }
            }


            ProjectSale sale = new ProjectSale();
            sale.setTotal(projectFee.getTotal());
            sale.setName(projectFee.getRemark());
            sale.setStatus("1");
            sale.setSaleDate(projectFee.getSubmitDate());
            sale.setAmount(1);
            saleList.add(sale);
        }

        BigDecimal totalCost = BigDecimal.ZERO; // 记录所有设备的总成本
        BigDecimal total = BigDecimal.ZERO;//销售总金额
        BigDecimal profit = BigDecimal.ZERO;//毛利
        // 2.循环所有的出库单 已经处理过所有数据
        fifoForBatchAll(saleList);

        //"0001900033" 运费

        for (ProjectSale sale : saleList) {
            String goodsId = sale.getGoodsId();
            if (StringUtils.isEmpty(goodsId)) {
                continue;
            }
            if ("00021".equals(goodsId)) {
                sale.setCostTotal(BigDecimal.ZERO);
                setInvoice(sale);
                continue;
            }

            total = total.add(sale.getTotal());
            profit = profit.add(sale.getProfit());
            totalCost = totalCost.add(sale.getCostTotal());

            if (sale.getProfit().compareTo(BigDecimal.ZERO) < 0) {
                // profit 小于 0 的逻辑
                sale.setStatus("1");
            }

        }

        BigDecimal grossProfit = total.subtract(totalCost);
//        BigDecimal grossMargin = grossProfit.divide(total, 2, RoundingMode.HALF_UP);

        BigDecimal grossMargin;

        if (total.compareTo(BigDecimal.ZERO) == 0) {
            // 分母为 0，根据业务需求处理：可设为 0，或 null，或抛出特定异常，或返回 NaN 类似值
            grossMargin = BigDecimal.ZERO; // 常见做法：毛利率为 0
            // 或者：grossMargin = BigDecimal.valueOf(Double.NaN);（不推荐用于计算）
            // 或者抛出自定义异常
        } else {
            grossMargin = grossProfit.divide(total, 2, RoundingMode.HALF_UP);
        }

        BigDecimal profitRatio = grossMargin.multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP);

        projectCustom.setProfitRatio(profitRatio);
        projectCustom.setProfit(profit);
        projectCustom.setCostTotal(totalCost);
        //销售总金额 没加其它收入
        projectCustom.setTotal(total);
        //更新信息
        customerService.updateById(projectCustom);
        map.put("projectCustom", projectCustom);
        map.put("saleList", saleList);


        //计算管家婆应收金额


        map.put("should", BigDecimal.ZERO);
        map.put("actual", BigDecimal.ZERO);
        map.put("remaining", BigDecimal.ZERO);
        map.put("gjpShould", BigDecimal.ZERO);
        map.put("same", new BigDecimal("2"));


        //应收金额
        BigDecimal should = BigDecimal.ZERO;
        //实收金额
        BigDecimal actual = BigDecimal.ZERO;
        //待收金额
        BigDecimal remaining = BigDecimal.ZERO;
        //合同金额
        //付款除去的金额 保函之类的
        BigDecimal payTotal = BigDecimal.ZERO;


        //管家婆实收金额  销售总金额减去 - 实收金额
        BigDecimal gjpShould = BigDecimal.ZERO;

        //减去调平金额 在前端减

        // actual = actual.subtract(projectCustom.getSubAmount());

        map.put("actual", actual);
        map.put("remaining", remaining);
        map.put("gjpShould", gjpShould);
        map.put("same", new BigDecimal("2"));

        if (StringUtils.isNotEmpty(customerId)) {
            ProjectFeeQuery projectFeeQuery = new ProjectFeeQuery();
            projectFeeQuery.setHousekeeperProject(customerId);
            List<ProjectFee> feeListAll = feeService.getReceiveAndOtherFee(projectFeeQuery);
            //累加实收金额actual
            BigDecimal other = BigDecimal.ZERO;

            //优惠的
            BigDecimal discount = feeService.getDiscount(projectFeeQuery);

            for (ProjectFee projectFee : feeListAll) {
                //其他收入里面  还需要过滤掉不在日期范围内的
                // 获取三个日期
                Date submitDate = projectFee.getSubmitDate();

// 判断 submitDate 是否在 [startDate, endDate] 范围内（包含边界）
                boolean isWithinRange = false;

                if (submitDate != null && startDate != null && endDate != null) {
                    isWithinRange = !submitDate.before(startDate) && !submitDate.after(endDate);
                    // 等价于：
                    isWithinRange = (submitDate.compareTo(startDate) >= 0) && (submitDate.compareTo(endDate) <= 0);
                    if (!isWithinRange) {
                        continue;
                    }
                }

                BigDecimal curentTotal = projectFee.getTotal();
                if (projectFee.getMainType().equals("4")) {//加上付款除去的
                    actual = actual.add(curentTotal);
                } else if (projectFee.getMainType().equals("93")) {//其他收入  已经加在了销售总金额里面
                    other = other.add(curentTotal);
//                    actual = actual.add(curentTotal);
                } else if (projectFee.getMainType().equals("66")) {//付出去的
                    payTotal = payTotal.add(curentTotal);
                    actual = actual.add(curentTotal);
                }

            }

            //计算待收金额 应收-实收
            // 执行减法操作
            map.put("remaining", remaining);
            map.put("actual", actual);

            //销售总金额
            BigDecimal saleTotal = BigDecimal.ZERO;

            saleTotal = total.add(other);
            //管家婆应收金额   销售总金额(加上其他收入)减去 - 实收金额 - 优惠的金额
            gjpShould = saleTotal.subtract(actual);
            map.put("gjpShould", gjpShould.subtract(discount));

            BigDecimal diff = gjpShould.subtract(remaining).abs();

            // 判断差值是否小于等于一块钱
            if (diff.compareTo(new BigDecimal("1")) <= 0) {
                map.put("same", new BigDecimal("1"));//表示待收金额和管家婆应收金额相等
            } else {
                map.put("same", new BigDecimal("2"));//不相等
            }
        }


        redisCache.redisTemplate.opsForValue().set(
                String.valueOf(projectCustom.getId()),
                map,
                12,
                TimeUnit.HOURS
        );

        return map;
    }


    @Override
    public Map<String, Object> saleConfigInfo(ProjectCustomQuery query) {
        Map<String, Object> map = new HashMap<>();
        //1.查询详细
        ProjectCustom projectCustom = customerService.getById(query.getId());
        //2.取出往来客户id 起始日期 截止日期
        String customerId = projectCustom.getCustomerId();
        Date startDate = projectCustom.getStartDate();
        Date endDate = projectCustom.getEndDate();

        //3.查询范围内的出库单
        List<ProjectSale> saleList = projectSaleMapper.getAllSaleCustom(customerId, projectCustom.getStartDate(), projectCustom.getEndDate());

        //4.直接累加 或者调用算法
        ProjectFeeQuery feeQuery = new ProjectFeeQuery();
        feeQuery.setHousekeeperProject(customerId);
        List<ProjectFee> feeList = feeService.getOtherFee(feeQuery);
        for (ProjectFee projectFee : feeList) {
            //其他收入里面  还需要过滤掉不在日期范围内的
            // 获取三个日期
            Date submitDate = projectFee.getSubmitDate();

// 判断 submitDate 是否在 [startDate, endDate] 范围内（包含边界）
            boolean isWithinRange = false;

            if (submitDate != null && startDate != null && endDate != null) {
                isWithinRange = !submitDate.before(startDate) && !submitDate.after(endDate);
                // 等价于：
                isWithinRange = (submitDate.compareTo(startDate) >= 0) && (submitDate.compareTo(endDate) <= 0);
                if (!isWithinRange) {
                    continue;
                }
            }


            ProjectSale sale = new ProjectSale();
            sale.setTotal(projectFee.getTotal());
            sale.setName(projectFee.getRemark());
            sale.setStatus("1");
            sale.setSaleDate(projectFee.getSubmitDate());
            sale.setAmount(1);
            saleList.add(sale);
        }

        if (StringUtils.isNotEmpty(query.getExcludeFeeKey())){
            List<ProjectSale> saleConfigInfo = getSaleConfigInfo(query, saleList);
            map.put("projectCustom", projectCustom);
            map.put("saleList", saleConfigInfo);
            return map;
        }


        for (ProjectSale sale : saleList) {
            String goodsId = sale.getGoodsId();
            if (StringUtils.isEmpty(goodsId)) {
                continue;
            }
            if ("00021".equals(goodsId)) {
                sale.setCostTotal(BigDecimal.ZERO);
                setInvoice(sale);
                continue;
            }

            if (sale.getAmount() != null) {
                sale.setAmount(-sale.getAmount());
                sale.setTotal(sale.getTotal().negate());

            }

        }
        map.put("projectCustom", projectCustom);
        map.put("saleList", saleList);
        return map;
    }

    public List<ProjectSale> getSaleConfigInfo(ProjectCustomQuery query, List<ProjectSale> saleList) {

        //排除掉已经被其它收款绑定的
        ProjectFeeQuery feeReceive = new ProjectFeeQuery();
        feeReceive.setMainId(query.getId());
        //排除自身的 和已经被其它fee绑定的
        String excludeFeeKey = query.getExcludeFeeKey();
        List<ProjectPay> receiveForCustomer = feeService.getReceiveAndPayForCustomer(feeReceive);

        List<String> resultSaleKeys = new ArrayList<>();
        for (ProjectPay fee : receiveForCustomer) {

            if (fee.getRemark().contains("调平")){
                continue;
            }

            String housekeeperProject = fee.getHousekeeperProject();
            BigDecimal total = fee.getTotal();
            String useType = fee.getUseType();
            String mainType = fee.getMainType();
            String submitDate = fee.getSubmitDate();
            String feeKey = String.join("|",
                    Objects.toString(housekeeperProject, "null"),
                    total == null ? "null" : total.toPlainString(),
                    Objects.toString(useType, "null"),
                    Objects.toString(mainType, "null"),
                    Objects.toString(submitDate, "null")
            );
            System.out.println(feeKey);

            //排除自身和调平金额
            if ((StringUtils.isNotEmpty(excludeFeeKey) && excludeFeeKey.equals(feeKey)) || fee.getRemark().contains("调平")) {
                continue;
            }

            LambdaQueryWrapper<ProjectSaleConfig> configWrapper = new LambdaQueryWrapper<>();
            configWrapper.eq(ProjectSaleConfig::getFeeKey, feeKey);
            try {
                ProjectSaleConfig configOne = saleConfigService.getOne(configWrapper);
                if (configOne != null) {
                    List<String> saleKey = configOne.getSaleKey();
                    resultSaleKeys.addAll(saleKey);
                }
            } catch (Exception e) {
                throw new RuntimeException("服务器错误,请联系管理员!");
            }

        }

        List<ProjectSale> resultSaleList = new ArrayList<>();
        for (ProjectSale sale : saleList) {
            String goodsId = sale.getGoodsId();
            if (StringUtils.isEmpty(goodsId)) {
                continue;
            }
            if ("00021".equals(goodsId)) {
                sale.setCostTotal(BigDecimal.ZERO);
                setInvoice(sale);
                continue;
            }

            if (sale.getAmount() != null) {
                sale.setAmount(-sale.getAmount());
                sale.setTotal(sale.getTotal().negate());

            }


            Date saleDate = sale.getSaleDate();
            Integer amount = sale.getAmount();
            String housekeeperProject = sale.getHousekeeperProject();
            BigDecimal price = sale.getPrice();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String saleDateStr = saleDate != null ? dateFormat.format(saleDate) : "null";
            String priceStr = price != null ? price.toPlainString() : "null";
            String itemKey = String.join("|",
                    Objects.toString(goodsId, "null"),
                    saleDateStr,
                    Objects.toString(amount, "null"),
                    Objects.toString(housekeeperProject, "null"),
                    priceStr
            );
            // 判断是否包含
            if (resultSaleKeys.contains(itemKey)) {
                // 包含，执行你的逻辑
                System.out.println("已存在该 itemKey");
                continue;
            } else {
                // 不包含
                System.out.println("未找到该 itemKey");
                resultSaleList.add(sale);
            }

        }
        return resultSaleList;
    }

    @Override
    public Map<String, BigDecimal> warrantyMoney(String projectNo) {
        Map<String, BigDecimal> map = new HashMap<>();
        if (StringUtils.isEmpty(projectNo)) {
            throw new RuntimeException("系统出错,请联系管理员排查");
        }

        Project projectInfo = projectService.getById(projectNo);
        String housekeeperProject = projectInfo.getHousekeeperProject();

        BigDecimal totalWarranty = BigDecimal.ZERO;
        BigDecimal receivedWarranty = BigDecimal.ZERO;
        LambdaQueryWrapper<ProjectAccounting> wrapper = new QueryWrapper<ProjectAccounting>().lambda()
                .eq(ProjectAccounting::getProjectNo, projectNo);
        List<ProjectAccounting> list = accountingService.list(wrapper);
        for (ProjectAccounting accounting : list) {
            totalWarranty = totalWarranty.add(accounting.getRetentionMoney());
        }
        map.put("receivedWarranty", BigDecimal.ZERO);
        map.put("totalWarranty", BigDecimal.ZERO);
        //取最后一次收款的金额为质保金实收
        LambdaQueryWrapper<ProjectFee> feeWrapper = new QueryWrapper<ProjectFee>().lambda()
                .eq(ProjectFee::getHousekeeperProject, housekeeperProject)
                .eq(ProjectFee::getUseType, "1")
                .in(ProjectFee::getMainType, Arrays.asList("4"))
                .gt(ProjectFee::getTotal, 0.1)
                .orderByDesc(ProjectFee::getSubmitDate) // 改为降序排序，最新的排在前面
                .last("LIMIT 1"); // 限制只返回一条记录
        ProjectFee fee = feeService.getOne(feeWrapper);
        if (fee != null) {
            receivedWarranty = fee.getTotal() != null ? fee.getTotal() : BigDecimal.ZERO;
            map.put("receivedWarranty", receivedWarranty);
        }
        map.put("totalWarranty", totalWarranty);
        return map;
    }


    /**
     * 1.项目税额=结算金额/(1+税率/100)*税率/100；
     * 2.毛利润=结算金额-项目材料成本-其他成本1一其他成本2-其他成本3 - 外协人工成本 - 资金成本
     * 3.项目成本税费=(项目材料成本 + 其他成本1 + 其他成本2 + 其他成本3 + 外协人工成本 + 资金成本)/(1+税率/100)*税率/100
     * 4.差额税费=项目税额-项目成本税费
     * 5.税前利润=毛利润-差额税费
     * 6.纯利润扣税金(也叫纯利润扣质保金)=税前利润*80% - 质保金  （产品销售）
     * 纯利润扣税金=税前利润*85%   -质保金（工程安装费）
     * 纯利润扣税金=税前利润*88%   -质保金（技术服务费）
     */
    public ProjectAccounting calculate(ProjectAccounting projectAccounting) {
        // 1. 计算项目税额 = 结算金额/(1+税率/100)*税率/100
        BigDecimal taxAmount = calculateTaxAmount(projectAccounting.getProjectAmount(), projectAccounting.getRate());
        projectAccounting.setTaxAmount(taxAmount);

        // 2. 计算毛利润 = 结算金额 - 项目材料成本 - 其他成本1 - 其他成本2 - 其他成本3 - 外协人工成本 - 资金成本
        BigDecimal grossProfit = calculateGrossProfit(
                projectAccounting.getProjectAmount(),
                projectAccounting.getMaterialCost(),
                projectAccounting.getCostOne(),
                projectAccounting.getCostTwo(),
                projectAccounting.getCostThree(),
                projectAccounting.getLaborCost(),
                projectAccounting.getFundCost()
        );
        projectAccounting.setProfit(grossProfit);

        // 3. 计算项目成本税费 = 项目材料成本/(1+税率/100)*税率/100
        //(项目材料成本 + 其他成本1 + 其他成本2 + 其他成本3 + 外协人工成本 + 资金成本)/(1+税率/100)*税率/100
        BigDecimal costFees = calculateCostFees(
                projectAccounting.getMaterialCost(),
                projectAccounting.getRate(),
                projectAccounting.getCostOne(),
                projectAccounting.getCostTwo(),
                projectAccounting.getCostThree(),
                projectAccounting.getLaborCost(),
                projectAccounting.getFundCost()
        );
        projectAccounting.setCostFees(costFees);

        // 4. 计算差额税费 = 项目税额 - 项目成本税费
        BigDecimal differenceFees = taxAmount.subtract(costFees);
        projectAccounting.setDifferenceFees(differenceFees);

        // 5. 计算税前利润 = 毛利润 - 差额税费
        BigDecimal beforeTaxProfit = grossProfit.subtract(differenceFees);
        projectAccounting.setBeforeProfit(beforeTaxProfit);

        // 6. 根据类型计算纯利润扣税金
        BigDecimal afterTaxProfit = calculateAfterTaxProfit(
                beforeTaxProfit,
                projectAccounting.getType(),
                projectAccounting.getRetentionMoney()
        );
        projectAccounting.setAfterProfit(afterTaxProfit);

        // 7. 计算项目提成（纯利润扣税金 * 25%）
        BigDecimal commissionRate = new BigDecimal("0.25");
        BigDecimal commission = afterTaxProfit.multiply(commissionRate).setScale(2, RoundingMode.HALF_UP);
        projectAccounting.setProjectCommission(commission);

        return projectAccounting;
    }

    /**
     * 计算项目税额
     * 公式：结算金额/(1+税率/100)*税率/100
     */
    public BigDecimal calculateTaxAmount(BigDecimal projectAmount, BigDecimal rate) {
        if (projectAmount == null || rate == null) {
            return BigDecimal.ZERO;
        }
        // 1 + 税率/100
        BigDecimal denominator = BigDecimal.ONE.add(rate.divide(BigDecimal.valueOf(100), 10, RoundingMode.HALF_UP));
        // 结算金额 / (1 + 税率/100)
        BigDecimal divided = projectAmount.divide(denominator, 10, RoundingMode.HALF_UP);
        // 结果 * 税率/100
        return divided.multiply(rate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
    }

    /**
     * 计算毛利润
     * 公式：结算金额 - 项目材料成本 - 其他成本1 - 其他成本2 - 其他成本3
     */
    public BigDecimal calculateGrossProfit(
            BigDecimal projectAmount,
            BigDecimal materialCost,
            BigDecimal costOne,
            BigDecimal costTwo,
            BigDecimal costThree,
            BigDecimal laborCost,
            BigDecimal fundCost
    ) {
        BigDecimal result = projectAmount;
        result = subtractIfNotNull(result, materialCost);
        result = subtractIfNotNull(result, costOne);
        result = subtractIfNotNull(result, costTwo);
        result = subtractIfNotNull(result, costThree);
        result = subtractIfNotNull(result, laborCost);
        result = subtractIfNotNull(result, fundCost);
        return result.setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 计算项目成本税费
     * 公式：项目材料成本/(1+税率/100)*税率/100
     * (项目材料成本 + 其他成本1 + 其他成本2 + 其他成本3 + 外协人工成本 + 资金成本)/(1+税率/100)*税率/100  20250515改
     */
    public BigDecimal calculateCostFees(BigDecimal materialCost, BigDecimal rate, BigDecimal one, BigDecimal two, BigDecimal three, BigDecimal laborCost, BigDecimal fundCost) {
        if (materialCost == null || rate == null) {
            return BigDecimal.ZERO;
        }

        if (one == null) {
            one = BigDecimal.ZERO;
        }

        if (two == null) {
            two = BigDecimal.ZERO;
        }

        if (three == null) {
            three = BigDecimal.ZERO;
        }

        if (laborCost == null) {
            laborCost = BigDecimal.ZERO;
        }

        if (fundCost == null) {
            fundCost = BigDecimal.ZERO;
        }


        materialCost = materialCost.add(one).add(two).add(three).add(laborCost).add(fundCost);

        // 1 + 税率/100
        BigDecimal denominator = BigDecimal.ONE.add(rate.divide(BigDecimal.valueOf(100), 10, RoundingMode.HALF_UP));
        // 项目材料成本 / (1 + 税率/100)
        BigDecimal divided = materialCost.divide(denominator, 10, RoundingMode.HALF_UP);
        // 结果 * 税率/100
        return divided.multiply(rate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
    }

    /**
     * 根据类型计算纯利润扣税金
     */
    public BigDecimal calculateAfterTaxProfit(BigDecimal beforeTaxProfit, String type, BigDecimal warranty) {
        if (beforeTaxProfit == null || type == null) {
            return BigDecimal.ZERO;
        }

        BigDecimal factor;
        switch (type) {
            case "13": // 产品销售
                factor = new BigDecimal("0.80");
                break;
            case "9":  // 工程安装费
                factor = new BigDecimal("0.85");
                break;
            case "6":  // 技术服务费
                factor = new BigDecimal("0.88");
                break;
            default:
                throw new IllegalArgumentException("未知的项目类型: " + type);
        }

        BigDecimal afterTax = beforeTaxProfit.multiply(factor).setScale(2, RoundingMode.HALF_UP);

        // 减去 warranty（如果非空）
        return subtractIfNotNull(afterTax, warranty);

    }

    /**
     * 安全减法（处理null值）
     */
    public BigDecimal subtractIfNotNull(BigDecimal base, BigDecimal subtractor) {
        return subtractor != null ? base.subtract(subtractor) : base;
    }


    // 辅助类，用于分组累加
    private static class GroupedCosting {
        private ProjectCosting example;
        private BigDecimal totalCount = BigDecimal.ZERO;

        public void addCosting(ProjectCosting costing) {
            if (example == null) {
                example = costing; // 保存第一个遇到的示例对象
            }
            totalCount = totalCount.add(costing.getCount());
        }

        public ProjectCosting getExample() {
            return example;
        }

        public BigDecimal getTotalCount() {
            return totalCount;
        }
    }

    @Override
    public TableDataInfo listCompare(ProjectAccountingQuery query) {

        Page<Object> page = PageHelper.startPage(query.getPageNum(), query.getPageSize());
        String projectNo = query.getProjectNo();

        Project projectInfo = projectService.getById(projectNo);
        String housekeeperProject = projectInfo.getHousekeeperProject();

        //比较正式清单和出库单

        //从缓存中取 0否1是
        if (!"1".equals(query.getRefresh())) {
            List<ProjectCosting> casheResultCostingList = redisCache.getCacheObject("listCompare" + housekeeperProject);
            if (casheResultCostingList != null) {
                return new TableDataInfo(casheResultCostingList, page.getTotal());
            }
        }

        if (StringUtils.isEmpty(housekeeperProject)) {
            throw new RuntimeException("请先关联管家婆项目");
        }

        //1.查询正式清单按照备注编码统计相同设备总共有多少数量
        LambdaQueryWrapper<ProjectCosting> costingWrapper = new QueryWrapper<ProjectCosting>().lambda()
                .eq(ProjectCosting::getProjectNo, projectNo);
        List<ProjectCosting> costingList = projectCostingService.list(costingWrapper);

        List<ProjectCosting> resultCostingList = new ArrayList<>();
        Map<String, GroupedCosting> groupedMap = new HashMap<>();
        List<ProjectCosting> emptyRemarkCostings = new ArrayList<>();

// 第一次遍历，按remarkCode分组并累加count，同时收集无remarkCode的项
        for (ProjectCosting costing : costingList) {
            String remarkCode = costing.getRemarkCode();
            if (StringUtils.isEmpty(remarkCode)) {
                emptyRemarkCostings.add(costing);
            } else {
                groupedMap.computeIfAbsent(remarkCode, k -> new GroupedCosting())
                        .addCosting(costing);
            }
        }

// 处理分组后的项
        for (Map.Entry<String, GroupedCosting> entry : groupedMap.entrySet()) {
            String remarkCode = entry.getKey();
            GroupedCosting grouped = entry.getValue();
            ProjectCosting example = grouped.getExample();
            BigDecimal totalCount = grouped.getTotalCount();

            ProjectCosting newCosting = new ProjectCosting();
            // 复制原对象的属性（根据实际情况调整需要复制的字段）
            BeanUtils.copyProperties(example, newCosting);
            newCosting.setCount(totalCount); // 设置累加的count
            newCosting.setId(null); // 若ID是唯一标识，需置空或重新生成

            // 根据remarkCode查询goodsId
            LambdaQueryWrapper<ProjectGoods> goodsWrapper = new LambdaQueryWrapper<>();
            goodsWrapper.eq(ProjectGoods::getModal, remarkCode);
            try {
                if (StringUtils.isNotEmpty(remarkCode)) {
                    List<ProjectGoods> goodsList = projectGoodsService.list(goodsWrapper);
                    for (ProjectGoods goods : goodsList) {
                        String id = goods.getId();
                        LambdaQueryWrapper<ProjectSale> salesWrapper = new LambdaQueryWrapper<>();
                        salesWrapper.eq(ProjectSale::getGoodsId, id);
                        salesWrapper.eq(ProjectSale::getHousekeeperProject, housekeeperProject);
                        List<ProjectSale> saleList = projectSaleService.list(salesWrapper);
                        for (ProjectSale sale : saleList) {
                            String goodsId = sale.getGoodsId();
                            newCosting.setGoodsId(goodsId);
                            break;
                        }
                    }
                }
                //备注编码不是唯一的 跟出库单的再对比

                // 无论是否找到goods，都将合并后的项添加到结果列表
                resultCostingList.add(newCosting);
            } catch (Exception e) {
                throw new RuntimeException("查询设备出错，请联系管理员");
            }
        }

// 添加无remarkCode的项
        resultCostingList.addAll(emptyRemarkCostings);


        //合并出库单
        LambdaQueryWrapper<ProjectSale> saleWrapper = new QueryWrapper<ProjectSale>().lambda()
                .eq(ProjectSale::getHousekeeperProject, housekeeperProject);
        List<ProjectSale> saleList = projectSaleService.list(saleWrapper);
        // 遍历原始列表，按 goodsId 累加 amount
        Map<String, Map<String, Object>> statsMap = new HashMap<>();
        fifoForBatchAll(saleList);
        for (ProjectSale sale : saleList) {
            String goodsId = sale.getGoodsId();
            if (StringUtils.isNotEmpty(goodsId)) {
                if (goodsId.equals("00021")) {
                    setInvoice(sale);
                    continue;
                }

            }
            // 获取或初始化内层Map（Key为固定字段名，如"amount"、"costTotal"等）
            Map<String, Object> stats = statsMap.computeIfAbsent(goodsId, k -> new HashMap<>());

            // 累加数量
            BigDecimal currentAmount = (BigDecimal) stats.getOrDefault("amount", BigDecimal.ZERO);
            stats.put("amount", currentAmount.add(new BigDecimal(sale.getAmount())));

            // 累加总成本（BigDecimal）
            BigDecimal currentCostTotal = (BigDecimal) stats.getOrDefault("costTotal", BigDecimal.ZERO);
            stats.put("costTotal", currentCostTotal.add(sale.getCostTotal()));

            // 累加销售总金额（BigDecimal）
            BigDecimal currentCostPrice = (BigDecimal) stats.getOrDefault("total", BigDecimal.ZERO);
            stats.put("total", currentCostPrice.add(sale.getTotal()));

            // 累加毛利（BigDecimal）
            BigDecimal currentProfit = (BigDecimal) stats.getOrDefault("profit", BigDecimal.ZERO);
            stats.put("profit", currentProfit.add(sale.getProfit()));

            // 设置单价（取最后一次的值，若需累加需调整逻辑）
            stats.put("price", sale.getPrice()); // 注意：这里直接覆盖，非累加
        }

// 将统计结果映射到 resultCostingList
        for (ProjectCosting costing : resultCostingList) {
            String goodsId = costing.getGoodsId();
            //先统一设置为0
            costing.setStatus("0");

            //备注编码是空的
            if (StringUtils.isEmpty(costing.getRemarkCode())) {
                costing.setStatus("1");
            }

            //并且要在出库单中包含的
            if (StringUtils.isNotEmpty(goodsId)) {
                // 根据 goodsId 获取统计信息
                Map<String, Object> stats = statsMap.get(goodsId);
                if (stats != null) {
                    // 设置统计值（处理可能的空值）
                    BigDecimal amount = (BigDecimal) stats.getOrDefault("amount", BigDecimal.ZERO);
                    costing.setSaleCount(amount);
                    costing.setCostTotal(((BigDecimal) stats.getOrDefault("costTotal", BigDecimal.ZERO)));
                    costing.setTotal(((BigDecimal) stats.getOrDefault("total", BigDecimal.ZERO)));
                    costing.setProfit((BigDecimal) stats.getOrDefault("profit", BigDecimal.ZERO));
                    costing.setPrice((BigDecimal) stats.getOrDefault("price", BigDecimal.ZERO));

                    //如果清单数量大于出库数量 标记绿色
//                    if (costing.getCount() > costing.getSaleCount()) {
//                        costing.setStatus("2");
//                    } else if (costing.getCount() < costing.getSaleCount()) {
//                        //如果清单数量小于出库数量 标记红色
//                        costing.setStatus("3");
//                    }
                    BigDecimal count = costing.getCount();
                    BigDecimal saleCount = costing.getSaleCount();

// 判空处理（视你的业务是否允许 null）
                    if (count == null) count = BigDecimal.ZERO;
                    if (saleCount == null) saleCount = BigDecimal.ZERO;
                    if (count.compareTo(saleCount) > 0) {
                        // 清单数量 > 出库数量
                        costing.setStatus("2");
                    } else if (count.compareTo(saleCount) < 0) {
                        // 清单数量 < 出库数量（标记红色）
                        costing.setStatus("3");
                    } else {
                        // 可选：相等的情况
                        // costing.setStatus("1"); 或其他状态
                    }

                }
            } else {
                //表示在出库单中找不到的
                costing.setStatus("1");
            }


        }


        // 如果出库单中有但正式清单中没有，添加并标记红色
// 收集已存在的goodsId（排除空值）
        Set<String> existingGoodsIds = resultCostingList.stream()
                .map(ProjectCosting::getGoodsId)
                .filter(goodsId -> StringUtils.isNotEmpty(goodsId))
                .collect(Collectors.toSet());

// 遍历出库单统计信息，检查未在正式清单中的项
        for (Map.Entry<String, Map<String, Object>> entry : statsMap.entrySet()) {
            String goodsId = entry.getKey();
            if (!existingGoodsIds.contains(goodsId)) {
                Map<String, Object> stats = entry.getValue();

                // 查询对应的ProjectGoods信息
                ProjectGoods goods = projectGoodsService.getById(goodsId);
                if (goods == null) {
                    continue; // 跳过无效goodsId
                }

                // 创建新Costing项并填充出库数据
                ProjectCosting newCosting = new ProjectCosting();
                BeanUtils.copyProperties(goods, newCosting, "id"); // 复制设备信息
                newCosting.setGoodsId(goodsId);
                newCosting.setRemarkCode(goods.getModal());
                newCosting.setCount(BigDecimal.ZERO); // 正式清单数量为0

                // 设置出库统计值（取负数）
                BigDecimal amount = (BigDecimal) stats.getOrDefault("amount", BigDecimal.ZERO);
                newCosting.setSaleCount(amount);
                newCosting.setCostTotal(((BigDecimal) stats.get("costTotal")));
                newCosting.setTotal(((BigDecimal) stats.get("total")));
                newCosting.setProfit((BigDecimal) stats.get("profit"));
                newCosting.setPrice((BigDecimal) stats.get("price"));

                newCosting.setStatus("3"); // 标记红色
                resultCostingList.add(newCosting);
            }
        }

        //如果有其他收入的 加进去
        ProjectFeeQuery feeQuery = new ProjectFeeQuery();
        feeQuery.setHousekeeperProject(housekeeperProject);
        List<ProjectFee> feeList = feeService.getOtherFee(feeQuery);
        for (ProjectFee fee : feeList) {
            ProjectCosting feeCosting = new ProjectCosting();
            feeCosting.setName(fee.getRemark());
            feeCosting.setTotal(fee.getTotal());
//            feeCosting.setCount(BigDecimal.ONE);
//            feeCosting.setSaleCount(BigDecimal.ONE);
            resultCostingList.add(feeCosting);
        }

        redisCache.redisTemplate.opsForValue().set("listCompare" + housekeeperProject, resultCostingList, 12, TimeUnit.HOURS);
        return new TableDataInfo(resultCostingList, page.getTotal());
    }


    @Override
    public List<ProjectSale> getAllGjpListCustomer(ProjectSaleQuery query) {
        List<ProjectSale> allGjpSale = projectSaleMapper.getAllSaleCustom(query.getHousekeeperProject(), query.getStartDate(), query.getEndDate());
// 遍历结果集，设置 goodsAllName
        for (ProjectSale sale : allGjpSale) {
            // 获取需要拼接的字段
            String name = sale.getName();
            String fullName = sale.getFullName();
            String modal = sale.getModal();
            String param = sale.getParam();

            // 拼接 goodsAllName，主键用 '-' 连接
            StringBuilder goodsAllNameBuilder = new StringBuilder();
            if (fullName != null && !fullName.isEmpty()) {
                goodsAllNameBuilder.append(fullName).append("-");
            }
            if (name != null && !name.isEmpty()) {
                goodsAllNameBuilder.append(name).append("-");
            }
            if (modal != null && !modal.isEmpty()) {
                goodsAllNameBuilder.append(modal).append("-");
            }
            if (param != null && !param.isEmpty()) {
                goodsAllNameBuilder.append(param);
            }

            // 移除末尾多余的 '-'
            String goodsAllName = goodsAllNameBuilder.toString();
            if (goodsAllName.endsWith("-")) {
                goodsAllName = goodsAllName.substring(0, goodsAllName.length() - 1);
            }

            // 设置拼接后的值到 goodsAllName
            sale.setGoodsAllName(goodsAllName);
        }


        fifoForBatchAll(allGjpSale);

        return allGjpSale;
    }


}
